import java.util.*;

class dbProcessorV2 extends Proc {
	public Proc sendTo;
	public int processor_ID;
	
	public int numberInQueue_job0;
	public int numberInQueue_job1;
	public int numberInQueue_job2;
	
	public int numberMaxUpdate;
	public int locationUpdateDb = 0;
	
	
	private final int JOB_TYPE_0 = 0;
	private final int JOB_TYPE_1 = 1;
	private final int JOB_TYPE_2 = 2;
		
	public LinkedList<Job> queue = new LinkedList<Job>();
	
	Random slump = new Random();

	dbProcessorV2(Proc sendTo, int id) {
		this.sendTo = sendTo;
		this.processor_ID = id;
		numberInQueue_job0 = 0;
		numberInQueue_job1 = 0;
		numberInQueue_job2 = 0;
	}

	public void TreatSignal(Signal x) {
		switch (x.signalType) {
		
		case G.ARRIVAL_JOB_0: {
			
				queue.add(new Job(JOB_TYPE_0, x.arrivalTime, x.retrial));
				numberInQueue_job0++;
				
//				System.out.println("Queue size " + numberInQueue_job0); 
				if (queue.size() == 1) {
					SignalList.SendSignal(G.READY_JOB_0, x.destination, G.time
							+ G.SERVICE_TIME_JOB0, x.arrivalTime, x.retrial);		
					
			}
		}break;
		
		case G.READY_JOB_0: {	
			numberInQueue_job0--;

			if(!overloadAlgorithm()){
				  Job rmJob = queue.remove();
				  if(rmJob.jobType != JOB_TYPE_0)
					  System.out.println("ERROR - FALSE JOB"); 
				  sendRetrial(x);
				  G.numberRejected++;
				  setNextJob(x);
			}
			else {	
				SignalList.SendSignal(G.READY_JOB_1, x.destination, G.time + G.SERVICE_TIME_JOB1, x.totalArrivalTime, x.retrial);
				queue.getFirst().jobType = JOB_TYPE_1;
				numberInQueue_job1++;
			}
		}break;
		
		case G.ARRIVAL_JOB_1: {
//			int all = numberInQueue_job1+numberInQueue_job2;
//			System.out.println("numberInQueue_job1 " + numberInQueue_job1); 
			
			queue.add(new Job(JOB_TYPE_1, x.arrivalTime, x.retrial));
			numberInQueue_job1++;
			
			if (queue.size() == 1) {
				SignalList.SendSignal(G.READY_JOB_1, x.destination, G.time
						+ G.SERVICE_TIME_JOB1, x.arrivalTime, x.retrial);
			}
		}break;
		
		case G.READY_JOB_1: {
			// find the db_processor containing subscriber info.
			//If this db_processor contains the subscriber data (with probability 1/N)
			//then we are done(it does nothing else).
			//if not, it sends a job_2 to the db_processor which contains the subscriber data
			int idDataProcessor = setDestination();   //Random destination with probability = 1/N
			//			System.out.println("sent to " + idDataProcessor + " ID " + processor_ID); 
			if( idDataProcessor != this.processor_ID){
				//				this.sendTo = ArrivalProcessor.dbProcessorNetwork[idDataProcessor];

				SignalList.SendSignal(G.ARRIVAL_JOB_2, ArrivalProcessor.dbProcessorNetwork[idDataProcessor], G.time, x.totalArrivalTime, x.retrial);
			}
			else{
				//				System.out.println("New arrival time " + (x.arrivalTime - x.totalArrivalTime));
				if((G.time - x.totalArrivalTime) > G.MAX_UPDATE_TIME) {
					sendRetrial(x);
					G.numberMaxUpdate++;
				}
				else {
					G.numberOfUpdates++;	
					G.avTime += (G.time - x.totalArrivalTime);
					locationUpdateDb++;
				}
			}
			numberInQueue_job1--;
			Job rmJob = queue.remove();
			if(rmJob.jobType != JOB_TYPE_1)
				System.out.println("ERROR - FALSE JOB"); 
			
			setNextJob(x);
		}break;
		
		case G.ARRIVAL_JOB_2:{
			
			queue.add(new Job(JOB_TYPE_2, x.arrivalTime, x.retrial));
			numberInQueue_job2++;
			
//	     	System.out.println("New arrival time " + x.arrivalTime + " ID " + processor_ID);
			
			if (queue.size() == 1) {
				SignalList.SendSignal(G.READY_JOB_2, x.destination, G.time
						+ G.SERVICE_TIME_JOB2, x.totalArrivalTime, x.retrial);
			}
		}break;
		
		case G.READY_JOB_2:{
			
//			System.out.println("New arrival time " + (G.time - x.totalArrivalTime));
	
			if((G.time - x.totalArrivalTime > G.MAX_UPDATE_TIME)) {
				sendRetrial(x);
				numberMaxUpdate++;
			}
			else {
				G.numberOfUpdates++;
				locationUpdateDb++;
				G.avTime += (G.time - x.totalArrivalTime);
			}

			numberInQueue_job2--;
			Job rmJob = queue.remove();			
			if(rmJob.jobType != JOB_TYPE_2)
				System.out.println("ERROR - FALSE JOB"); 
			
			setNextJob(x);
		}break;
		
		}
		
	}
	
	private void sendRetrial(Signal x) {
		if(!x.retrial & G.RETRIAL_ALG) {
//			System.out.println("RETRIAL"); 
			switch (G.OVERLOAD_ALGORITHM) {
				case G.TOTAL: {
					SignalList.SendSignal(G.ARRIVAL_JOB_0, ArrivalProcessor.dbProcessorNetwork[setDestination()], G.time + G.RETRIAL_TIME, x.arrivalTime, true);
				}break;
				case G.WEIGHTED: {
					SignalList.SendSignal(G.ARRIVAL_JOB_0, ArrivalProcessor.dbProcessorNetwork[setDestination()], G.time + G.RETRIAL_TIME, x.arrivalTime, true);
				}break;
				default: {
					SignalList.SendSignal(G.ARRIVAL_JOB_1, ArrivalProcessor.dbProcessorNetwork[setDestination()], G.time + G.RETRIAL_TIME, x.arrivalTime, true);	
				}break;
			}
		}
	}
	
	private void setNextJob(Signal x) {
		if(!queue.isEmpty()) {
			Job rmJob = queue.getFirst();
			switch(rmJob.jobType) {
			case JOB_TYPE_0: {
				SignalList.SendSignal(G.READY_JOB_0, x.destination, G.time
						+ G.SERVICE_TIME_JOB0, rmJob.arrivalTime, rmJob.retrial);
			}break;
			case JOB_TYPE_1: {
				SignalList.SendSignal(G.READY_JOB_1, x.destination, G.time
						+ G.SERVICE_TIME_JOB1, rmJob.arrivalTime, rmJob.retrial);
			}break;
			case JOB_TYPE_2: {
				SignalList.SendSignal(G.READY_JOB_2, x.destination, G.time
						+ G.SERVICE_TIME_JOB2, rmJob.arrivalTime, rmJob.retrial);
			}break;
			}
		}
	}

	private int setDestination() {
		return slump.nextInt(G.NUMBER_OF_DB_PROCESSORS);
	}
	
	// Overload control algorithm: true -> accept
	private boolean overloadAlgorithm() {
		switch (G.OVERLOAD_ALGORITHM) {
		case G.TOTAL: {
			return G.LIMIT >= (numberInQueue_job0 + numberInQueue_job1) + numberInQueue_job2;
		}
		case G.WEIGHTED: {
			return G.LIMIT >= G.WEIGHT_1 * (numberInQueue_job0 + numberInQueue_job1) + G.WEIGHT_2
					* numberInQueue_job2;
		}
		default:
			// no control;
			return true;	

		}
	}

}

class Job {
	public int jobType = 0;
	public double arrivalTime = 0;
	public boolean retrial = false;
	
	Job(int jobType, double arrivalTime, boolean retrial) {
		this.jobType = jobType;
		this.arrivalTime = arrivalTime;
		this.retrial = retrial;
	}
}